local snow     = require("meet_your_mods.mhr").snow
local status   = require("meet_your_mods.status")
local shortcut = require("meet_your_mods.shortcut")
local routine  = require("meet_your_mods.routine_framework")
local ui       = require("meet_your_mods.ui")

snow.data.DataDef.CustomBuildupCategoryId:fetch_enum("defence", "CustomBuildupCategory_013")
snow.data.DataDef.CustomBuildupCategoryId:fetch_enum("element", "CustomBuildupCategory_014")
snow.data.DataDef.CustomBuildupCategoryId:fetch_enum("slot", "CustomBuildupCategory_019")
snow.data.DataDef.CustomBuildupCategoryId:fetch_enum("skill", "CustomBuildupCategory_020")
snow.data.DataDef.CustomBuildupCategoryId:fetch_enum("special", "CustomBuildupCategory_015")
snow.data.DataDef.ArmorElementRegistTypes:fetch_enum("min", "Fire")
snow.data.DataDef.ArmorElementRegistTypes:fetch_enum("max", "Dragon")

local equipment = {
    config = {
        armor = nil,
    },

    data = {
        armors     = nil,
        armor      = nil,
        armor_page = 1,
    },
}

--读取怪异炼化配置
function equipment:load_config()
    if not self.config.armor then
        self.config.armor = self:load_armor_config()
    end
end

--读取防具怪异炼化配置
function equipment:load_armor_config()
    local config = {
        series   = {},
        part     = { "头盔", "胸甲", "护手", "裙甲", "腿甲", },
        element  = { "火", "水", "雷", "冰", "龙", },
        page     = { "已拥有的　", "炼化开启的", "已装备的　" },
        category = {
            ui   = { "------", "防御力", "耐性", "插槽", "技能", "耐性惩罚",},
            data = {
                snow.data.DataDef.CustomBuildupCategoryId.defence,
                snow.data.DataDef.CustomBuildupCategoryId.element,
                snow.data.DataDef.CustomBuildupCategoryId.slot,
                snow.data.DataDef.CustomBuildupCategoryId.skill,
                snow.data.DataDef.CustomBuildupCategoryId.special,
            },
            hash = {},
        },
        id_value = nil,
        skill    = nil,
    }

    config.id_value = self:load_armor_idvalue_config(config.category)
    config.skill    = self:load_armor_skill_config()

    return config
end

--读取防具怪异炼化选项配置
function equipment:load_armor_idvalue_config(category_supported)
    --支持的词条池
    local pool_id = { 1, 2, 3, 4, 5, 6, 13, }
    --ui文字
    local label = {
        [94] = "火耐性", [89] = "火耐性", [90] = "火耐性", [91] = "火耐性",
        [104] = "水耐性", [99] = "水耐性", [100] = "水耐性", [101] = "水耐性",
        [114] = "雷耐性", [109] = "雷耐性", [110] = "雷耐性", [111] = "雷耐性",
        [124] = "冰耐性", [119] = "冰耐性", [120] = "冰耐性", [121] = "冰耐性",
        [134] = "龙耐性", [129] = "龙耐性", [130] = "龙耐性", [131] = "龙耐性",
        [144] = "D级技能", [145] = "C级技能", [146] = "B级技能",
        [147] = "A级技能", [148] = "S级技能", [149] = "移除技能",
    }

    local id_value = {}
    for _, id in pairs(pool_id) do
        local config = snow.data.CustomBuildupModule:getArmorData(id)
        local pool   = {
            data = { {}, {}, {}, {}, {}, }, --词条的id和value index
            ui   = { {}, {}, {}, {}, {}, }, --每个id和value index的显示文字
            hash = { {}, {}, {}, {}, {}, }, --用于以id和value index查询data索引的映射
        }

        for index, category in pairs(category_supported.data) do
            for _, sdk_config in pairs(config._DataDict[category]) do
                sdk_config = sdk_config._BaseParam

                for value_index, value in pairs(sdk_config._ValueTable) do
                    if 0 == value then
                        break
                    end

                    local data = { id = sdk_config._Id, cost = sdk_config._Cost, index = value_index - 1, }
                    local text = label[data.id] or ""
                    category_supported.hash[data.id] = index

                    if snow.data.DataDef.CustomBuildupCategoryId.slot == category then
                        text = text .. string.format("%+d", value)
                    elseif snow.data.DataDef.CustomBuildupCategoryId.defence == category then
                        if 70 == data.id or 71 == data.id or 72 == data.id then
                            text = text .. string.format("%+d(结算%+d)", value, data.cost)
                        else
                            text = text .. string.format("%+d(点数%+d)", value, data.cost)
                        end
                    elseif snow.data.DataDef.CustomBuildupCategoryId.element == category
                        or snow.data.DataDef.CustomBuildupCategoryId.special == category
                    then
                        text = text .. string.format(" %+d", value)
                    end

                    table.insert(pool.data[index], data)
                    table.insert(pool.ui[index], text)
                    pool.hash[index][self:make_idvalue_key(data.id, data.index)] = #pool.data[index]
                end
            end
        end

        id_value[id] = pool
    end

    return id_value
end

function equipment:make_idvalue_key(id, value_index)
    return id * 10 + value_index
end

--读取防具怪异炼化技能配置
function equipment:load_armor_skill_config()
    --技能cost映射
    local cost  = { [3] = 1, [6] = 2, [9] = 3, [12] = 4, [15] = 5, }
    local skill = {
        data = { {}, {}, {}, {}, {}, },
        ui   = { {}, {}, {}, {}, {}, },
        hash = { {}, {}, {}, {}, {}, },
    }

    for _, config in pairs(snow.data.CustomBuildupModule:get_singleton()._EquipSkillDetailUserData._Param) do
        local index = cost[config._Cost]
        if index then
            table.insert(skill.data[index], config._SkillId)
            table.insert(skill.ui[index], shortcut:get_equipment_skill_name(config._SkillId))
            skill.hash[index][config._SkillId] = #skill.data[index]
        end
    end

    return skill
end

--读取装备箱中的防具
function equipment:load_armors()
    if status.logical_status.none == status:current_status() or not self.config.armor then
        self.data.armors     = nil
        self.data.armor      = nil
        self.data.armor_page = 1
        return
    end

    local armors = {
        {}, --存储所有防具
        {}, --存储已开启怪异炼化的防具
        { --存储当前装备防具
            {
                part   = { {}, {}, {}, {}, {}, },
                length = 0,
            },
        },
    }
    for _, equipment in pairs(shortcut:get_equipment_inventory()) do
        if not equipment:isArmor() then
            goto next_equipment
        end

        local armor = equipment:getArmorData()
        if not armor:isEnableCustom() then
            goto next_equipment
        end

        local part    = armor:getPartsType() + 1
        local series  = self:get_armor_series(armor)
        local content = self:load_armor(armor, series)

        local all, enabled = self:get_armor_list(series, armors)
        local equipped     = armors[3][1]

        --插入全列表
        table.insert(all.part[part], content)
        all.length = math.max(all.length, #all.part[part])

        --插入已开启列表
        if content.result.enabled then
            table.insert(enabled.part[part], content)
            enabled.length = math.max(enabled.length, #enabled.part[part])
        end

        --插入已装备列表
        if equipment:isEquiped() then
            table.insert(equipped.part[part], content)
            equipped.length = math.max(equipped.length, #equipped.part[part])
        end
        ::next_equipment::
    end

    --将全列表和已开启列表按照防具系列的顺序排序
    table.sort(armors[1], function(left, right) return left.series.sort < right.series.sort end)
    table.sort(armors[2], function(left, right) return left.series.sort < right.series.sort end)

    if self.data.armor_page < 1 then
        self.data.armor_page = 1
    elseif self.data.armor_page > #armors then
        self.data.armor_page = #armors
    end
    self.data.armors = armors
end

--获取装备系列列表
function equipment:get_armor_series(armor)
    local series_id = armor:getArmorSeries()
    local config    = self.config.armor.series[series_id]
    if not config then
        local series_data = shortcut:get_equipment_series(series_id)
        config = {
            name = series_data:get_SeriesName(),
            sort = series_data:get_SortId(),
            uuid = ui:generate_uuid()
        }
        self.config.armor.series[series_id] = config
    end

    return config
end

--获取给定系列的防具列表
function equipment:get_armor_list(series, armors)
    local armor_lists = {
        armors[1], --所有防具列表
        armors[2], --开启炼化的防具列表
    }
    local result_lists = {}

    for index, armor_list in pairs(armor_lists) do
        for _, armors in pairs(armor_list) do
            if series == armors.series then
                result_lists[index] = armors
            end
        end
        if result_lists[index] then
            goto next_list
        end

        --新建防具列表
        result_lists[index] = {
            series = series,
            part   = { {}, {}, {}, {}, {}, },
            length = 0,
        }
        table.insert(armor_list, result_lists[index])
        ::next_list::
    end

    return table.unpack(result_lists)
end

--读取给定防具
function equipment:load_armor(armor, series, content)
    if not content then
        local part = armor:getPartsType() + 1
        content    = {
            series   = series,
            name     = shortcut:get_armor_name(armor:get_Id()),
            part     = part,
            uuid     = series.uuid .. "/" .. part,
            instance = armor:get_RefInventory(),
        }
    end

    content.result  = self:load_armor_customize_result(armor)
    content.tooltip = self:make_armor_description(content)

    return content
end

--读取给定防具的炼化结果
function equipment:load_armor_customize_result(armor)
    local result = {
        enabled    = armor:get_CustomEnable(),
        customized = armor:get_CustomBuildupResultNum() > 0,

        defence = 0,
        element = {},
        slot    = {},
        skill   = { up = {}, down = {}, }
    }

    if not result.enabled then
        return result
    end

    --获取防御力炼化结果
    result.defence = armor:get_CustomAddDef()

    --获取抗性炼化结果
    for element = snow.data.DataDef.ArmorElementRegistTypes.min, snow.data.DataDef.ArmorElementRegistTypes.max do
        table.insert(result.element, armor:getCustomAddReg(element))
    end

    --获取炼化后的插槽
    for level_index, slot in pairs(armor:get_DecorationSlotNumList()) do
        for _ = 1, slot do
            table.insert(result.slot, level_index)
        end
    end
    while #result.slot < 3 do table.insert(result.slot, 0) end
    table.sort(result.slot, function(left, right) return left > right end)

    --计算插槽炼化结果
    for slot_index, level in pairs(armor:getOriginalSlotLvTable()) do
        result.slot[slot_index] = result.slot[slot_index] - level
    end

    --获取技能炼化结果
    local skill_list = {
        { output = result.skill.up, input = armor:getCustomSkillUpList(), },
        { output = result.skill.down, input = armor:getCustomSkillDownList(), },
    }
    for _, list in pairs(skill_list) do
        for _, skill in pairs(list.input) do
            table.insert(list.output, { name = skill:get_Name(), level = skill:get_TotalLv(), })
        end
    end

    return result
end

--为给定防具设生成描述
function equipment:make_armor_description(content)
    local description = string.format("<%s>　%s\n%s\n",
        content.series.name, self.config.armor.part[content.part], content.name)

    if not content.result.enabled then
        return description .. "\n尚未开启炼化"
    end
    if not content.result.customized then
        return description .. "\n尚未进行炼化"
    end

    description = description .. string.format("\n防御：%+d\n抗性：", content.result.defence)

    for index, element in pairs(content.result.element) do
        description = description .. string.format("%s%+d/", self.config.armor.element[index], element)
    end
    description = string.sub(description, 1, -2) .. string.format("\n插槽：+%d +%d +%d",
        content.result.slot[1], content.result.slot[2], content.result.slot[3])

    local skills = ""
    for _, skill_list in pairs({ content.result.skill.up, content.result.skill.down, }) do
        for _, skill in pairs(skill_list) do
            skills = skills .. string.format("　　　%s%+d\n", skill.name, skill.level)
        end
    end
    if skills ~= "" then
        description = description .. "\n技能：\n" .. skills
    end

    return description
end

function equipment:initialize()
    routine:hook(self)

    if status:current_status() ~= status.logical_status.none then
        self:post_before_village()
    end

    ui:cluster("inventory", "存档功能")
    ui:button("inventory", "防具炼化", self.toggle_armor_window, self)
    ui:sameline("inventory")
    ui:button("inventory", "导出炼化防具", self.export_armors, self)

    ui:child_window(function() return self.data.armors end, function() self:toggle_armor_window() end,
        "怪异炼化编辑", self.draw_armor_window, self)
end

function equipment:normalize()
end

function equipment:pre_return_title(_)
    self.config.armor = nil
    self.data.armors  = nil
    self.data.armor   = nil
    self.data.index   = 1
end

function equipment:post_before_village(_)
    self:load_config()
end

function equipment:toggle_armor_window()
    if self.data.armors then
        self.data.armors = nil
        self.data.armor  = nil
    else
        self:load_armors()
    end
end

--绘制防具怪异炼化编辑窗口
function equipment:draw_armor_window()
    imgui.spacing()
    imgui.indent(4)

    if not self.data.armor then
        self:draw_armor_info_view()
    else
        self:draw_armor_edit_view(self.data.armor)
    end
end

--绘制防具展示视图
function equipment:draw_armor_info_view()
    --绘制页面切换控件
    if imgui.button("＜") then
        self.data.armor_page = self.data.armor_page - 1 >= 1 and self.data.armor_page - 1 or #self.data.armors
    end
    imgui.same_line()
    imgui.text(self.config.armor.page[self.data.armor_page])
    imgui.same_line()
    if imgui.button("＞") then
        self.data.armor_page = self.data.armor_page + 1 <= #self.data.armors and self.data.armor_page + 1 or 1
    end

    imgui.same_line()
    imgui.spacing()
    imgui.same_line()
    if imgui.button("刷新") then
        self:load_armors()
    end

    --绘制防具表格
    imgui.spacing()
    if not imgui.begin_table(nil, 6,
        imgui.TableFlags.NoSavedSettings | imgui.TableFlags.RowBg | imgui.TableFlags.BordersInnerV)
    then
        return
    end

    --绘制表头
    imgui.table_setup_column("系列", imgui.ColumnFlags.WidthStretch)
    for _, part in pairs(self.config.armor.part) do
        imgui.table_setup_column(part, imgui.ColumnFlags.WidthFixed)
    end
    imgui.table_headers_row()

    for _, series in pairs(self.data.armors[self.data.armor_page]) do
        if series.length ~= 0 then
            self:draw_armors(series)
        end
    end
    imgui.end_table()
end

--绘制系列防具
function equipment:draw_armors(series)
    --绘制系列名称
    imgui.table_next_column()
    imgui.text(series.series and series.series.name or self.config.armor.page[self.data.armor_page])

    for index = 1, series.length do
        if index > 1 then
            imgui.table_next_column()
            imgui.text(nil)
        end

        for _, armors in pairs(series.part) do
            imgui.table_next_column()
            local armor = armors[index]
            if not armor then
                imgui.text(nil)
                goto next_part
            end

            imgui.push_id(armor.uuid)
            if imgui.button("编辑") then
                self:edit_armor(armor)
            end
            if imgui.is_item_hovered() then
                imgui.set_tooltip(armor.tooltip)
            end
            imgui.pop_id()
            ::next_part::
        end
    end
end

--绘制防具编辑视图
function equipment:draw_armor_edit_view(armor)
    imgui.text("系列／部位：" .. armor.series.name .. "／" .. self.config.armor.part[armor.part])
    imgui.text("　　　名称：" .. armor.name)
    imgui.text("　　　点数：" .. string.format("%d／%d", armor.editor.used, armor.editor.cost))

    imgui.spacing()
    if imgui.button("返回") then
        self:load_armor(armor.instance:getArmorData(), nil, armor)
        armor.editor    = nil
        self.data.armor = nil
        return
    end

    imgui.same_line()
    imgui.spacing()
    imgui.same_line()
    if not armor.result.enabled then
        if not imgui.button("开启炼化") then
            return
        end
        self:enable_armor_customize(armor)
    elseif imgui.button("重置") then
        self:reset_armor_customize(armor)
    end

    imgui.text(nil)
    if not imgui.begin_table(nil, 5, imgui.TableFlags.NoSavedSettings | imgui.TableFlags.BordersInnerH) then
        return
    end
    imgui.table_setup_column(nil, imgui.ColumnFlags.WidthFixed)
    imgui.table_setup_column("词条类别", imgui.ColumnFlags.WidthStretch)
    imgui.table_setup_column("词条", imgui.ColumnFlags.WidthStretch)
    imgui.table_setup_column("技能", imgui.ColumnFlags.WidthStretch)
    imgui.table_setup_column("点数", imgui.ColumnFlags.WidthFixed)
    imgui.table_headers_row()

    for index, step in pairs(armor.editor.step) do
        imgui.table_next_column()
        imgui.text(index)

        imgui.table_next_column()
        imgui.set_next_item_width(-1)
        self:draw_armor_category(armor, step, index)

        imgui.table_next_column()
        imgui.set_next_item_width(-1)
        self:draw_armor_idvalue(armor, step, index)

        imgui.table_next_column()
        imgui.set_next_item_width(-1)
        self:draw_armor_skill(armor, step, index)

        imgui.table_next_column()
        imgui.text(step.cost and string.format("%+d", step.cost))
    end
    imgui.table_next_row()
    imgui.table_set_column_index(4)
    imgui.text(armor.editor.used)

    imgui.end_table()
end

function equipment:draw_armor_category(armor, step, index)
    imgui.push_id(armor.uuid .. "/category/" .. index)
    local changed, value = imgui.combo(nil, step.category + 1, self.config.armor.category.ui)
    imgui.pop_id()

    if not changed or value - 1 == step.category then
        return
    end
    step.category = value - 1
    step.index    = step.category ~= 0 and 1 or nil
    step.skill    = 4 == step.category and 1 or nil

    local config = step.index and armor.editor.pool.data[step.category][step.index] or nil
    step.cost    = config and config.cost or nil

    step.instance._Id         = config and config.id or 0
    step.instance._ValueIndex = config and config.index or 0

    if 4 == step.category then
        step.instance._SkillId = armor.editor.skill.data[step.index][step.skill]
    else
        step.instance._SkillId = 0
    end
    armor.editor.quantity = armor.editor.quantity + (config and 1 or -1)

    self:on_armor_customized(armor)
end

function equipment:draw_armor_idvalue(armor, step, index)
    if not step.index then
        imgui.text(nil)
        return
    end

    imgui.push_id(armor.uuid .. "/idvalue/" .. index)
    local changed, value = imgui.combo(nil, step.index, armor.editor.pool.ui[step.category])
    imgui.pop_id()

    if not changed or value == step.index then
        return
    end
    step.index = value
    step.skill = 4 == step.category and 1 or nil

    local config              = armor.editor.pool.data[step.category][step.index]
    step.cost                 = config.cost
    step.instance._Id         = config.id
    step.instance._ValueIndex = config.index

    if 4 == step.category then
        step.instance._SkillId = armor.editor.skill.data[step.index][step.skill]
    else
        step.instance._SkillId = 0
    end
    self:on_armor_customized(armor)
end

function equipment:draw_armor_skill(armor, step, index)
    if not step.skill then
        imgui.text(nil)
        return
    end

    imgui.push_id(armor.uuid .. "/skill/" .. index)
    local changed, value = imgui.combo(nil, step.skill, armor.editor.skill.ui[step.index])
    imgui.pop_id()

    if not changed or value == step.skill then
        return
    end
    step.skill = value

    step.instance._SkillId = armor.editor.skill.data[step.index][step.skill]
    self:on_armor_customized(armor)
end

--开始编辑指定防具
function equipment:edit_armor(content)
    local armor = content.instance:getArmorData()
    local base  = armor:get_BaseData()

    local editor = {
        pool  = self.config.armor.id_value[armor:get_CustomTableNo()],
        cost  = armor:get_CustomCost(),
        used  = armor:getCustomSpendCost(snow.data.DataDef.CustomBuildupCategoryId.CustomBuildupCategory_None),
        skill = {
            data = { table.unpack(self.config.armor.skill.data) },
            ui   = { table.unpack(self.config.armor.skill.ui) },
            hash = { table.unpack(self.config.armor.skill.hash) },
        }
    }

    --读取防具原始技能
    local data, ui, hash = {}, {}, {}
    for _, skill in pairs(base:get_AllSkillDataList()) do
        table.insert(data, skill:get_EquipSkillId())
        table.insert(ui, skill:get_Name())
        hash[skill:get_EquipSkillId()] = #data
    end
    table.insert(editor.skill.data, data)
    table.insert(editor.skill.ui, ui)
    table.insert(editor.skill.hash, hash)

    --读取炼化词条
    editor.step, editor.quantity = self:load_armor_customize_step(content, editor)

    content.editor  = editor
    self.data.armor = content
end

--读取防具怪异炼化词条
function equipment:load_armor_customize_step(content, editor)
    local step     = {}
    local quantity = 0
    for _, buildup in pairs(content.instance._CustomBuildup) do
        local category = self.config.armor.category.hash[buildup._Id] or 0

        local index = category ~= 0
            and editor.pool.hash[category][self:make_idvalue_key(buildup._Id, buildup._ValueIndex)] or nil
        local cost  = index and editor.pool.data[category][index].cost or nil
        local skill = 4 == category and index and editor.skill.hash[index][buildup._SkillId] or nil

        table.insert(step, {
            instance = buildup,
            category = category,
            index    = index,
            cost     = cost,
            skill    = skill,
        })

        quantity = quantity + (category ~= 0 and 1 or 0)
    end

    return step, quantity
end

--开启给定防具的怪异炼成
function equipment:enable_armor_customize(armor)
    armor.instance._CustomEnable = true
    armor.result.enabled = true

    --将防具插入到已开启列表中
    local enabled = select(2, self:get_armor_list(armor.series, self.data.armors))
    table.insert(enabled.part[armor.part], armor)
    enabled.length = math.max(enabled.length, #enabled.part[armor.part])
end

--重置给定防具的怪异炼成
function equipment:reset_armor_customize(armor)
    for _, step in pairs(armor.editor.step) do
        step.category = 0
        step.index    = nil
        step.cost     = nil
        step.skill    = nil

        step.instance._Id         = 0
        step.instance._ValueIndex = 0
        step.instance._SkillId    = 0
    end
    armor.editor.quantity = 0

    self:on_armor_customized(armor)
end

--重新读取防具怪异炼成信息
function equipment:on_armor_customized(armor)
    armor.editor.used = armor.instance:getArmorData():getCustomSpendCost(snow.data.DataDef.CustomBuildupCategoryId
        .CustomBuildupCategory_None)
end

--导出怪异炼化防具
function equipment:export_armors()
    if not self.config.armor then
        return
    end

    local contents = ""
    for _, equipment in pairs(shortcut:get_equipment_inventory()) do
        if not equipment:isArmor() or not equipment:get_CustomEnable() then
            goto next_inventory
        end
        local armor  = equipment:getArmorData()
        local result = self:load_armor_customize_result(armor)

        local content = string.format("%s,%d,", armor:getName(), result.defence)
        for _, element in pairs(result.element) do
            content = content .. element .. ","
        end

        for _, slot in pairs(result.slot) do
            content = content .. slot .. ","
        end

        local quantity = 0
        for _, skills in pairs({ result.skill.up, result.skill.down, }) do
            for _, skill in pairs(skills) do
                quantity = quantity + 1
                content  = content .. skill.name .. "," .. skill.level .. ","
            end
        end
        while quantity < 4 do
            quantity = quantity + 1
            content  = content .. ",,"
        end

        contents = contents .. content .. ",\n"
        ::next_inventory::
    end

    fs.write(string.format("meet_your_mods.armor.c%02d.txt", status:current_save_data()),
        string.sub(contents, 1, -2))
end

return equipment
